package comlib.shaper

import chisel3._
import chisel3.util._

import scala.collection.mutable.ArrayBuffer

/** Shaper设置类(抽象类)
 */
abstract class ShaperConfigItem(tokenSize:Int=8) extends Bundle {
  /** 填桶量计算 
   * @param args 计算填桶量参数
   *
   * @note 必须实现args.size=0条件。该条件用于其它的Shaper参数计算
   */
  def fillToken(args:UInt*) : UInt
  /** 最大正桶深 */
  val pbs = UInt(tokenSize.W)
  /** 最大负桶深 */
  val dfs = UInt(tokenSize.W)
  /** Shaper带宽计算 */
  def bandwidth(freq:Float, unit:Float) = fillToken().litValue().floatValue * unit / freq 
}

/** 寄存器实现的Shaper设置类
 * 
 * Feature
 * - 支持每cycle填桶
 * - 支持反压暂停填桶设置
 */
class RegShaperConfigItem(tokenSize:Int=8) extends ShaperConfigItem(tokenSize) {
  /** 每cycle填充量 */
  val token = UInt(tokenSize.W)

  /** 填桶量计算
   *
   * 收到反压则停止填桶，否则正常填桶。若不给反压信号，则当作正常填充，返回`token`值
   * @param args 仅支持一个UInt(1.W)类型的参数: 当前是否收到反压。
   */
  override def fillToken(args:UInt*) = {
    if( args.size == 0 ) token
    else Mux( args(0).asBool, 0.U, token )
  }

  override def cloneType = new RegShaperConfigItem(tokenSize).asInstanceOf[this.type]
}

/** Shaper状态记录类
  */
class ShaperStatus(tokenSize:Int=8) extends Bundle{
  /** 令牌值 */
  val token = RegInit(0.U(tokenSize.W))

  /** 读取令牌值 */
  def read = token

  /** 设置令牌值 */
  def write(tk:UInt) = token := tk

  override def cloneType = new ShaperStatus(tokenSize).asInstanceOf[this.type]
}


/** 事件填桶Shaper状态记录类
 *
 * 使用TS来记录上次填充的时间，然后每次调度后重新填充
 */
class EventShaperStatus(tokenSize:Int=8, tsSize:Int=16) extends ShaperStatus {
  /** 时戳 */
  val ts = RegInit(0.U(tsSize.W))

  override def cloneType = new EventShaperStatus(tokenSize, tsSize).asInstanceOf[this.type]
}


//==============================================================================================
// Shaper处理Middleware定义
//==============================================================================================

/** 填桶/扣桶默认参数 
 * @param status Shaper状态
 * @param cfg Shaper配置
 */
class ShaperProcessParam(
  val status : ShaperStatus,
  val cfg : ShaperConfigItem
)

/** Shaper处理装饰器基类
 * @param dct 下一跳装饰器 
 */
class ShaperDecorator(val dct:Option[ShaperDecorator]=None){

  /** 填桶函数
   *
   * 默认每次填1个单位token
   * @param params 填桶输入参数
   * @return 填桶token量
   */
  def fill( params:ShaperProcessParam ):UInt = {
    if( dct.isEmpty ) params.cfg.fillToken()
    else dct.get.fill( params )
  }

  /** 扣桶函数
   *
   * 默认每次扣1个单位token
   * @param params 扣桶输入参数
   * @return 扣桶token量
   */
  def defill( params:ShaperProcessParam ):UInt = {
    if( dct.isEmpty ) 1.U
    else dct.get.defill( params )
  }

  /** 更新函数 
   *
   * 就是同时fill和defill，并更新令牌桶的合并处理函数
   * @param params 输入参数
   * @return 当前Shaper输出状态
   */
  def update( params:ShaperProcessParam ) = 1.U

}

/** 反压停止填桶装饰器
 * @param xoff 反压信号
 */
class FillPause(val xoff:Bool, dct:Option[ShaperDecorator]=None) extends ShaperDecorator(dct){
  override def fill( params:ShaperProcessParam ):UInt = 0.U
}

/** 包长扣桶装饰器
 * @param len 出队包长
 */
class LengthDefill(val len:UInt, dct:Option[ShaperDecorator]=None) extends ShaperDecorator(dct){
  override def defill( params:ShaperProcessParam ):UInt = len
}
//==============================================================================================



/** Shaper抽象封装类
 *   - 桶深：决定了可以允许的前级突发量
 *   - 桶初始值：决定了本级对下级的突发
 */
abstract class Shaper(num:Int, maxThdNum:Int=1){
  /** 配置表 */
  val cfg = ArrayBuffer[ShaperConfigItem]()
  /** 模板表 */
  val tmpl = ArrayBuffer[UInt]()
  /** Token表 */
  val token = ArrayBuffer[ShaperStatus]()
  /** 状态表 */
  val postok = VecInit(Seq.tabulate(num)(_ => 0.U(maxThdNum.W)))

  /**填桶量计算
   * @param tsDiff 据上次填充的时间差
   * @param pause 暂停填充使能
   * @return 返回填桶token量
   */
  def fill(tsDiff:UInt=1.U, pause:Bool=false.B):UInt

  /**扣桶量计算
   * @return 返回扣桶token量
   */
  def defill() : UInt

  /**更新桶
   * @return 返回更新后的桶状态
   */
  def update() : UInt

  
}
